package com.ahdy.service;

import com.ahdy.pojo.AsrResponse;
import com.ahdy.tool.AudioConvertUtil;
import com.google.gson.Gson;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.ByteArrayInputStream;
import java.net.URL;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
public class XunfeiAsrService {

    // 讯飞参数（建议改为配置文件读取）
    private static final String hostUrl = "https://iat.cn-huabei-1.xf-yun.com/v1";
    private static final String appid = "e165b70d";
    private static final String apiSecret = "MTBlM2EwMjA5NjAxMDEzNWZlMDBkZDJl";
    private static final String apiKey = "60fc45cf4c9f18357c1d3b27c7371ec0";

    private static final int STATUS_FIRST_FRAME = 0;
    private static final int STATUS_CONTINUE_FRAME = 1;
    private static final int STATUS_LAST_FRAME = 2;

    private final Gson gson = new Gson();
    private final OkHttpClient httpClient = new OkHttpClient.Builder()
            .connectTimeout(10, TimeUnit.SECONDS)
            .readTimeout(60, TimeUnit.SECONDS)
            .writeTimeout(60, TimeUnit.SECONDS)
            .build();

    public AsrResponse recognize(MultipartFile audioFile) {
        String sessionId = "asr_" + System.currentTimeMillis() + "_" + UUID.randomUUID().toString().substring(0, 8);
        long startTime = System.currentTimeMillis();

        try {
            if (audioFile == null || audioFile.isEmpty()) {
                return AsrResponse.error("音频文件不能为空", sessionId);
            }
            byte[] audioData = audioFile.getBytes();
            // mp3自动转码为wav
            String filename = audioFile.getOriginalFilename();
            if (filename != null && filename.toLowerCase().endsWith(".mp3")) {
                try {
                    audioData = AudioConvertUtil.mp3ToWav(audioData);
                    log.info("已自动将mp3转码为16kHz单声道wav");
                } catch (Exception e) {
                    log.warn("mp3转码失败，直接用原始mp3识别", e);
                }
            }

            String result = recognizeWithWebSocket(audioData, sessionId);

            long duration = System.currentTimeMillis() - startTime;
            return AsrResponse.success(result, duration, sessionId);

        } catch (Exception e) {
            log.error("语音识别失败 - 会话ID: {}, 错误: {}", sessionId, e.getMessage(), e);
            return AsrResponse.error("语音识别失败：" + e.getMessage(), sessionId);
        }
    }

    private String recognizeWithWebSocket(byte[] audioData, String sessionId) throws Exception {
        String authUrl = getAuthUrl(hostUrl, apiKey, apiSecret);
        String wsUrl = authUrl.replace("http://", "ws://").replace("https://", "wss://");

        Request request = new Request.Builder().url(wsUrl).build();
        CompletableFuture<String> resultFuture = new CompletableFuture<>();
        StringBuilder resultBuilder = new StringBuilder();

        httpClient.newWebSocket(request, new WebSocketListener() {
            private int frameSize = 1280;
            private int interval = 40;
            private int status = STATUS_FIRST_FRAME;
            private int seq = 0;
            private ByteArrayInputStream audioStream;

            @Override
            public void onOpen(WebSocket webSocket, Response response) {
                audioStream = new ByteArrayInputStream(audioData);
                new Thread(() -> {
                    try {
                        sendAudioData(webSocket);
                    } catch (Exception e) {
                        resultFuture.completeExceptionally(e);
                    }
                }).start();
            }

            @Override
            public void onMessage(WebSocket webSocket, String text) {
                System.out.println("讯飞原始返回: " + text); // 打印原始返回
                try {
                    JsonParse jsonParse = gson.fromJson(text, JsonParse.class);
                    if (jsonParse != null && jsonParse.header.code == 0) {
                        if (jsonParse.payload != null && jsonParse.payload.result != null) {
                            if (jsonParse.payload.result.text != null) {
                                byte[] decodedBytes = Base64.getDecoder().decode(jsonParse.payload.result.text);
                                String decodeRes = new String(decodedBytes, StandardCharsets.UTF_8);
                                JsonParseText jsonParseText = gson.fromJson(decodeRes, JsonParseText.class);
                                if (jsonParseText != null && jsonParseText.ws != null) {
                                    for (Ws ws : jsonParseText.ws) {
                                        if (ws.cw != null) {
                                            for (Cw cw : ws.cw) {
                                                resultBuilder.append(cw.w);
                                            }
                                        }
                                    }
                                }
                            }
                            if (jsonParse.payload.result.status == 2) {
                                resultFuture.complete(resultBuilder.toString());
                                webSocket.close(1000, "识别完成");
                            }
                        }
                    } else if (jsonParse != null) {
                        resultFuture.completeExceptionally(new RuntimeException("讯飞错误: " + jsonParse.header.message));
                    }
                } catch (Exception e) {
                    resultFuture.completeExceptionally(e);
                }
            }

            @Override
            public void onFailure(WebSocket webSocket, Throwable t, Response response) {
                resultFuture.completeExceptionally(t);
            }

            private void sendAudioData(WebSocket webSocket) throws Exception {
                byte[] buffer = new byte[frameSize];
                while (true) {
                    seq++;
                    int len = audioStream.read(buffer);
                    if (len == -1) {
                        status = STATUS_LAST_FRAME;
                    }
                    String json;
                    switch (status) {
                        case STATUS_FIRST_FRAME:
                            json = buildFirstFrameJson(len, buffer);
                            status = STATUS_CONTINUE_FRAME;
                            break;
                        case STATUS_CONTINUE_FRAME:
                            json = buildContinueFrameJson(len, buffer);
                            break;
                        case STATUS_LAST_FRAME:
                            json = buildLastFrameJson();
                            webSocket.send(json);
                            return;
                        default:
                            continue;
                    }
                    webSocket.send(json);
                    Thread.sleep(interval);
                }
            }

            private String buildFirstFrameJson(int len, byte[] buffer) {
                return "{\n" +
                        "  \"header\": {\n" +
                        "    \"app_id\": \"" + appid + "\",\n" +
                        "    \"status\": " + STATUS_FIRST_FRAME + "\n" +
                        "  },\n" +
                        "  \"parameter\": {\n" +
                        "    \"iat\": {\n" +
                        "      \"domain\": \"slm\",\n" +
                        "      \"language\": \"mul_cn\",\n" +
                        "      \"accent\": \"mandarin\",\n" +
                        "      \"eos\": 6000,\n" +
                        "      \"vinfo\": 1,\n" +
                        "      \"result\": {\n" +
                        "        \"encoding\": \"utf8\",\n" +
                        "        \"compress\": \"raw\",\n" +
                        "        \"format\": \"json\"\n" +
                        "      }\n" +
                        "    }\n" +
                        "  },\n" +
                        "  \"payload\": {\n" +
                        "    \"audio\": {\n" +
                        "      \"encoding\": \"raw\",\n" +
                        "      \"sample_rate\": 16000,\n" +
                        "      \"channels\": 1,\n" +
                        "      \"bit_depth\": 16,\n" +
                        "      \"seq\": " + seq + ",\n" +
                        "      \"status\": 0,\n" +
                        "      \"audio\": \"" + Base64.getEncoder().encodeToString(Arrays.copyOf(buffer, len)) + "\"\n"
                        +
                        "    }\n" +
                        "  }\n" +
                        "}";
            }

            private String buildContinueFrameJson(int len, byte[] buffer) {
                return "{\n" +
                        "  \"header\": {\n" +
                        "    \"app_id\": \"" + appid + "\",\n" +
                        "    \"status\": 1\n" +
                        "  },\n" +
                        "  \"payload\": {\n" +
                        "    \"audio\": {\n" +
                        "      \"encoding\": \"raw\",\n" +
                        "      \"sample_rate\": 16000,\n" +
                        "      \"channels\": 1,\n" +
                        "      \"bit_depth\": 16,\n" +
                        "      \"seq\": " + seq + ",\n" +
                        "      \"status\": 1,\n" +
                        "      \"audio\": \"" + Base64.getEncoder().encodeToString(Arrays.copyOf(buffer, len)) + "\"\n"
                        +
                        "    }\n" +
                        "  }\n" +
                        "}";
            }

            private String buildLastFrameJson() {
                return "{\n" +
                        "  \"header\": {\n" +
                        "    \"app_id\": \"" + appid + "\",\n" +
                        "    \"status\": 2\n" +
                        "  },\n" +
                        "  \"payload\": {\n" +
                        "    \"audio\": {\n" +
                        "      \"encoding\": \"raw\",\n" +
                        "      \"sample_rate\": 16000,\n" +
                        "      \"channels\": 1,\n" +
                        "      \"bit_depth\": 16,\n" +
                        "      \"seq\": " + seq + ",\n" +
                        "      \"status\": 2,\n" +
                        "      \"audio\": \"\"\n" +
                        "    }\n" +
                        "  }\n" +
                        "}";
            }
        });

        // 等待结果，设置超时
        try {
            return resultFuture.get(60, TimeUnit.SECONDS);
        } catch (Exception e) {
            throw new RuntimeException("语音识别失败: " + e.getMessage());
        }
    }

    private String getAuthUrl(String hostUrl, String apiKey, String apiSecret) throws Exception {
        URL url = new URL(hostUrl);
        SimpleDateFormat format = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US);
        format.setTimeZone(TimeZone.getTimeZone("GMT"));
        String date = format.format(new Date());
        StringBuilder builder = new StringBuilder("host: ").append(url.getHost()).append("\n")
                .append("date: ").append(date).append("\n")
                .append("GET ").append(url.getPath()).append(" HTTP/1.1");
        Charset charset = Charset.forName("UTF-8");
        Mac mac = Mac.getInstance("hmacsha256");
        SecretKeySpec spec = new SecretKeySpec(apiSecret.getBytes(charset), "hmacsha256");
        mac.init(spec);
        byte[] hexDigits = mac.doFinal(builder.toString().getBytes(charset));
        String sha = Base64.getEncoder().encodeToString(hexDigits);
        String authorization = String.format("api_key=\"%s\", algorithm=\"%s\", headers=\"%s\", signature=\"%s\"",
                apiKey, "hmac-sha256", "host date request-line", sha);
        HttpUrl httpUrl = HttpUrl.parse("https://" + url.getHost() + url.getPath()).newBuilder()
                .addQueryParameter("authorization", Base64.getEncoder().encodeToString(authorization.getBytes(charset)))
                .addQueryParameter("date", date)
                .addQueryParameter("host", url.getHost())
                .build();
        return httpUrl.toString();
    }

    // 内部解析类
    static class JsonParse {
        Header header;
        Payload payload;
    }

    static class Header {
        int code;
        String message;
        String sid;
        int status;
    }

    static class Payload {
        Result result;
    }

    static class Result {
        String text;
        int status;
    }

    static class JsonParseText {
        List<Ws> ws;
        String pgs;
        List<Integer> rg;
    }

    static class Ws {
        List<Cw> cw;
    }

    static class Cw {
        String w;
    }

    // 服务可用性检查
    public boolean isServiceAvailable() {
        return true;
    }

    // 服务状态
    public Map<String, Object> getServiceStatus() {
        Map<String, Object> status = new HashMap<>();
        status.put("available", true);
        status.put("maxFileSize", "10MB");
        status.put("webSocketTimeout", 60000);
        status.put("hostUrl", hostUrl);
        return status;
    }
}
